#region Using Statements
using System;
using System.IO;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
#endregion

namespace SkyBurner
{
    #region CustomShader class
    public class CustomShader : BaseObject
    {
        protected String m_shaderName;
        protected Effect m_customEffect;

        public String ShaderName
        {
            get { return m_shaderName; }
        }

        public CustomShader()
        {

        }

        public virtual void Load(BaseGame pGame, String pEffectFile)
        {
            m_Game = pGame;
            m_shaderName = Path.GetFileName(pEffectFile);
            m_customEffect = m_Game.ContentManager.Load<Effect>(pEffectFile);
        }

    }
    #endregion

    #region MOODBlinnPhongShader class
    public class MOODBlinnPhongShader : CustomShader
    {
        public MOODBlinnPhongShader()
        {

        }

        public void Draw(GameTime gameTime, Camera pActiveCamera, Vector3 pEyePosition, Vector3 pLightPostion, MOODModel pMOODModel)
        {
            // Vector3 vLightPosition = pActiveCamera.Position + pLightPostion; 

            m_customEffect.Parameters["fvLightPosition"].SetValue(pLightPostion);
            m_customEffect.Parameters["fvEyePosition"].SetValue(pEyePosition);
            m_customEffect.Parameters["matWorld"].SetValue(pMOODModel.WorldMatrix);
            m_customEffect.Parameters["matView"].SetValue(pMOODModel.ViewMatrix);
            m_customEffect.Parameters["matProjection"].SetValue(pMOODModel.ProjectionMatrix);
            m_customEffect.Parameters["fvAmbient"].SetValue(new Vector4(0.7f, 0.7f, 0.7f, 1.0f));
            m_customEffect.Parameters["fvSpecular"].SetValue(new Vector4(0.7f, 0.7f, 0.7f, 1.0f));
            m_customEffect.Parameters["fvDiffuse"].SetValue(new Vector4(0.7f, 0.7f, 0.7f, 1.0f));
            m_customEffect.Parameters["fSpecularPower"].SetValue(15.0f);

            foreach (MOODGroup moodGroup in pMOODModel.Groups)
            {
                if (moodGroup.TriangleCount <= 0)
                    continue;

                m_Game.GraphicsDevice.VertexDeclaration = moodGroup.VertexDecl;

                MOODMaterial moodMaterial;
                if (moodGroup.MaterialIndex >= 0)
                {
                    moodMaterial = pMOODModel.Materials[moodGroup.MaterialIndex];
                    if (moodMaterial.Texture != null)
                        m_customEffect.Parameters["Texture"].SetValue(moodMaterial.Texture);

                    //m_moodBasicEffect.DiffuseColor = new Vector3(moodMaterial.mtDiffuse.X, moodMaterial.mtDiffuse.Y, moodMaterial.mtDiffuse.Z);
                    //m_moodBasicEffect.AmbientLightColor = new Vector3(moodMaterial.mtAmbient.X, moodMaterial.mtAmbient.Y, moodMaterial.mtAmbient.Z);
                    //m_moodBasicEffect.SpecularColor = new Vector3(moodMaterial.mtSpecular.X, moodMaterial.mtSpecular.Y, moodMaterial.mtSpecular.Z);
                }

                m_customEffect.Begin();
                foreach (EffectPass pass in m_customEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    m_Game.GraphicsDevice.Vertices[0].SetSource(moodGroup.VertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
                    m_Game.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, moodGroup.TriangleCount);
                    pass.End();
                }
                m_customEffect.End();
            }
        }
    }
    #endregion

    #region MOODParticleShader class
    public class MOODParticleShader : CustomShader
    {
        private MOODModel m_moodParticle;

        public MOODParticleShader()
        {
            
        }

        public override void Load(BaseGame pGame, string pEffectFile)
        {
            base.Load(pGame, pEffectFile);

            m_moodParticle = new MOODModel(pGame, "Data\\Models\\quad.mood");
        }

        public void Draw(GameTime gameTime, Camera pActiveCamera, ParticleEmitterBase pParticleEmitterBase, Particle pParticle)
        {
            m_customEffect.Parameters["World"].SetValue(Matrix.CreateBillboard(pParticle.Position, pActiveCamera.Position, pActiveCamera.Up, pActiveCamera.View));
            m_customEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(pActiveCamera.Position, pActiveCamera.LookAt, pActiveCamera.Up));
            m_customEffect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(pActiveCamera.FOV, pActiveCamera.AspectRatio, pActiveCamera.NearPlane, pActiveCamera.FarPlane));
            m_customEffect.Parameters["ParticleTexture"].SetValue(pParticleEmitterBase.ParticleTexture);
            m_customEffect.Parameters["Color"].SetValue(pParticle.Color);
            m_customEffect.Parameters["AlphaFactor"].SetValue(pParticle.AlphaFactor);
            m_customEffect.Parameters["Scale"].SetValue(Matrix.CreateScale(pParticle.Size));
            
            foreach (MOODGroup moodGroup in m_moodParticle.Groups)
            {
                if (moodGroup.TriangleCount <= 0)
                    continue;

                m_Game.GraphicsDevice.VertexDeclaration = moodGroup.VertexDecl;

                m_customEffect.Begin();
                foreach (EffectPass pass in m_customEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    m_Game.GraphicsDevice.Vertices[0].SetSource(moodGroup.VertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
                    m_Game.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, moodGroup.TriangleCount);
                    pass.End();
                }
                m_customEffect.End();
            }
        }
    }
    #endregion

    #region MOODShieldGlassShader class
    public class MOODShieldGlassShader : CustomShader
    {
        public MOODShieldGlassShader()
        {

        }

        public void Draw(GameTime gameTime, Camera pActiveCamera, Vector3 pEyePosition, float pAlphaFactor, TextureCube pCubeMap, Texture pRainbowTexture, MOODModel pMOODModel)
        {            
            m_customEffect.Parameters["matWorld"].SetValue(pMOODModel.WorldMatrix);
            m_customEffect.Parameters["matView"].SetValue(pMOODModel.ViewMatrix);
            m_customEffect.Parameters["matProjection"].SetValue(pMOODModel.ProjectionMatrix);
            m_customEffect.Parameters["fvEyePosition"].SetValue(pEyePosition);
            m_customEffect.Parameters["refractionScale"].SetValue(1.0f);
            m_customEffect.Parameters["rainbowScale"].SetValue(1.0f);
            m_customEffect.Parameters["baseColor"].SetValue(new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
            m_customEffect.Parameters["ambient"].SetValue(0.1f);
            m_customEffect.Parameters["indexOfRefractionRatio"].SetValue(1.14f);
            m_customEffect.Parameters["alphaFactor"].SetValue(pAlphaFactor);
            m_customEffect.Parameters["RainbowTexture"].SetValue(pRainbowTexture);
            m_customEffect.Parameters["SkyCubeTexture"].SetValue(pCubeMap);

            m_Game.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            m_Game.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
            m_Game.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            m_Game.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            m_Game.GraphicsDevice.RenderState.DepthBufferWriteEnable = false;

            foreach (MOODGroup moodGroup in pMOODModel.Groups)
            {
                if (moodGroup.TriangleCount <= 0)
                    continue;

                m_Game.GraphicsDevice.VertexDeclaration = moodGroup.VertexDecl;

                m_customEffect.Begin();
                foreach (EffectPass pass in m_customEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    m_Game.GraphicsDevice.Vertices[0].SetSource(moodGroup.VertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
                    m_Game.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, moodGroup.TriangleCount);
                    pass.End();
                }
                m_customEffect.End();
            }

            m_Game.GraphicsDevice.RenderState.AlphaTestEnable = false;
            m_Game.GraphicsDevice.RenderState.AlphaBlendEnable = false;
            m_Game.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
        }
    }
    #endregion

    #region PointSpriteShader class
    public class PointSpriteShader : CustomShader
    {
        private VertexPositionColor[] m_pointSpritePosition = new VertexPositionColor[1];

        public PointSpriteShader()
        {
            
        }

        public void Draw(GameTime gameTime, Camera pActiveCamera, ParticleEmitterBase pParticleEmitterBase, Particle pParticle)
        {
            m_customEffect.Parameters["World"].SetValue(Matrix.Identity);
            m_customEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(pActiveCamera.Position, pActiveCamera.LookAt, pActiveCamera.Up));
            m_customEffect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(pActiveCamera.FOV, pActiveCamera.AspectRatio, pActiveCamera.NearPlane, pActiveCamera.FarPlane));
            m_customEffect.Parameters["SpriteTexture"].SetValue(pParticleEmitterBase.ParticleTexture);
            m_customEffect.Parameters["AlphaFactor"].SetValue(pParticle.AlphaFactor);
            m_customEffect.Parameters["ParticleSize"].SetValue(pParticle.Size);
            m_customEffect.Parameters["ViewportHeight"].SetValue(m_Game.GraphicsDevice.Viewport.Height);
            m_customEffect.Parameters["Color"].SetValue(pParticle.Color);

            m_Game.GraphicsDevice.VertexDeclaration = new VertexDeclaration(m_Game.GraphicsDevice, VertexPositionColor.VertexElements);

            m_pointSpritePosition[0].Position = pParticle.Position;
            m_pointSpritePosition[0].Color = Color.White;

            m_customEffect.Begin();
            foreach (EffectPass pass in m_customEffect.CurrentTechnique.Passes)
            {
                pass.Begin();
                m_Game.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.PointList, m_pointSpritePosition, 0, 1);
                pass.End();
            }
            m_customEffect.End();
        }
    }
    #endregion

    #region VolumetricCloudShader class
    public class VolumetricCloudShader : CustomShader
    {
        private MOODModel m_moodParticle;

        public VolumetricCloudShader()
        {

        }

        public override void Load(BaseGame pGame, string pEffectFile)
        {
            base.Load(pGame, pEffectFile);

            m_moodParticle = new MOODModel(pGame, "Data\\Models\\quad.mood");
        }

        public void Draw(GameTime gameTime, Camera pActiveCamera, Vector3 pSunPosition, Vector3 pWorldPosition, Vector3 pParticlePosition, float pParticleSize, Texture2D pParticleTexture)
        {
            m_customEffect.Parameters["World"].SetValue(Matrix.CreateBillboard(pParticlePosition, pActiveCamera.Position, pActiveCamera.Up, pActiveCamera.View));
            m_customEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(pActiveCamera.Position, pActiveCamera.LookAt, pActiveCamera.Up));
            m_customEffect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(pActiveCamera.FOV, pActiveCamera.AspectRatio, pActiveCamera.NearPlane, pActiveCamera.FarPlane));
            m_customEffect.Parameters["Size"].SetValue(pParticleSize);
            m_customEffect.Parameters["SunPosition"].SetValue(pSunPosition);
            m_customEffect.Parameters["CloudParticlePosition"].SetValue(pParticlePosition);
            m_customEffect.Parameters["WorldCenter"].SetValue(Vector3.Zero);
            m_customEffect.Parameters["ParticleTexture"].SetValue(pParticleTexture);
            
            foreach (MOODGroup moodGroup in m_moodParticle.Groups)
            {
                if (moodGroup.TriangleCount <= 0)
                    continue;

                m_Game.GraphicsDevice.VertexDeclaration = moodGroup.VertexDecl;

                m_customEffect.Begin();
                foreach (EffectPass pass in m_customEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    m_Game.GraphicsDevice.Vertices[0].SetSource(moodGroup.VertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
                    m_Game.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, moodGroup.TriangleCount);
                    pass.End();
                }
                m_customEffect.End();
            }
        }
    }
    #endregion
}
